package net.jalbum.filters;

import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;

import javax.swing.ImageIcon;
import javax.swing.JPanel;

import net.jalbum.filterManager.FilterProperties.FilterCategory;
import static net.jalbum.filters.BasicFilter.convertType;

import se.datadosen.jalbum.Msg;

public class GammaFilterPlugin extends BasicFilter {

    /**
     *
     */
    private static final long serialVersionUID = 7863546541947760848L;
    protected GammaControl gammaControl;
    protected double gammaAlpha = 1;
    protected double gammaR = 1;
    protected double gammaG = 1;
    protected double gammaB = 1;

    public GammaFilterPlugin() {
        this.name = Msg.getString(this, "filters.gamma.name");
        this.shortName = Msg.getString(this, "filters.gamma.shortName");
        this.icon = new ImageIcon(this.getClass().getResource("res/levels.png"));
        this.author = "David Fichtmueller";
        this.version = "1.0";
        this.description = Msg.getString(this, "filters.gamma.description");
        this.category = FilterCategory.ADVANCED;
        this.prescale = false;
        this.postscale = true;
        this.gammaControl = new GammaControl(this);
    }

    @Override
    public JPanel getControls() {
        return this.gammaControl;
    }

    /**
     * We never touch the original image so avoid cloning
     *
     * @param bi
     * @return
     */
    @Override
    protected BufferedImage cloneBufferedImage(BufferedImage bi) {
        return convertType(bi, BufferedImage.TYPE_INT_ARGB);
    }

    @Override
    public BufferedImage renderImage(BufferedImage bi) {
        int dataType = bi.getSampleModel().getDataType();
        if (dataType != DataBuffer.TYPE_INT) {
            bi = convertType(bi, BufferedImage.TYPE_INT_ARGB);
        }
        
        final int w = bi.getWidth();
        final int h = bi.getHeight();
//        final BufferedImage out = new BufferedImage(w, h, bi.getType());
        final int[] pixels = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();

        final int[] alphas = new int[256];
        final int[] reds = new int[256];
        final int[] greens = new int[256];
        final int[] blues = new int[256];

        for (int i = 0; i < 256; i++) {
            alphas[i] = this.calculateGamma(i, this.gammaAlpha);
            reds[i] = this.calculateGamma(i, this.gammaR);
            greens[i] = this.calculateGamma(i, this.gammaG);
            blues[i] = this.calculateGamma(i, this.gammaB);
        }
//        final int[] pixels = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
//				int rgb = bi.getRGB(x, y);
                int rgb = pixels[y * w + x];
                int alpha = (int) ((rgb & 0xff000000l) >> 24);
                int r = ((rgb & 0x00ff0000) >> 16);
                int g = ((rgb & 0x0000ff00) >> 8);
                int b = ((rgb & 0x000000ff));

                alpha = alphas[alpha];
                r = reds[r];
                g = greens[g];
                b = blues[b];

                rgb = ((alpha & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
//				out.setRGB(x, y, rgb);
                pixels[y * w + x] = rgb;
            }
        }
        return bi;
    }

    /*
     * @Override public BufferedImage renderImage(BufferedImage bi) { final int w = bi.getWidth(); final int h = bi.getHeight();
     * 
     * BufferedImage out = new BufferedImage(w, h, bi.getType());
     * 
     * HashMap<Integer, Integer> alphaValues = new HashMap<Integer, Integer>(); HashMap<Integer, Integer> redValues = new HashMap<Integer, Integer>(); HashMap<Integer, Integer> greenValues = new HashMap<Integer, Integer>(); HashMap<Integer, Integer> blueValues = new
     * HashMap<Integer, Integer>();
     * 
     * for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { int rgb = bi.getRGB(x, y);
     *  // get the rgb-values int alphaOld = (int) ((rgb & 0xff000000l) >> 24); int alpha = alphaOld; if (alphaValues.containsKey(alphaOld)) { alpha = alphaValues.get(alphaOld); } else { alpha = calculateGamma(alphaOld, gammaAlpha); if (alpha > 255) { alpha = 255; } else if
     * (alpha < 0) { alpha = 0; } alphaValues.put(alphaOld, alpha); }
     * 
     * int rOld = ((rgb & 0x00ff0000) >> 16); int r = rOld; if (redValues.containsKey(rOld)) { r = redValues.get(rOld); } else { r = calculateGamma(rOld, gammaR); if (r > 255) { r = 255; } else if (r < 0) { r = 0; } redValues.put(rOld, r); }
     * 
     * int gOld = ((rgb & 0x0000ff00) >> 8); int g = gOld; if (greenValues.containsKey(gOld)) { g = greenValues.get(gOld); } else { g = calculateGamma(gOld, gammaG); if (g > 255) { g = 255; } else if (g < 0) { g = 0; } greenValues.put(gOld, g); }
     * 
     * int bOld = ((rgb & 0x000000ff)); int b = bOld; if (blueValues.containsKey(bOld)) { b = blueValues.get(bOld); } else { b = calculateGamma(bOld, gammaB); if (b > 255) { b = 255; } else if (b < 0) { b = 0; } blueValues.put(bOld, b); }
     * 
     * rgb = ((alpha & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); out.setRGB(x, y, rgb); } }
     * 
     * return out; }
     */
    private int calculateGamma(final int color, final double gamma) {
        int val = (int) (255 * Math.pow(color, 1 / gamma) / Math.pow(255, 1 / gamma));
        if (val > 255) {
            val = 255;
        } else if (val < 0) {
            val = 0;
        }
        return val;
    }

    public double getGammaAlpha() {
        return this.gammaAlpha;
    }

    public void setGammaAlpha(final double gammaAlpha) {
        this.gammaAlpha = gammaAlpha;
    }

    public double getGammaR() {
        return this.gammaR;
    }

    public void setGammaR(final double gammaR) {
        this.gammaR = gammaR;
    }

    public double getGammaG() {
        return this.gammaG;
    }

    public void setGammaG(final double gammaG) {
        this.gammaG = gammaG;
    }

    public double getGammaB() {
        return this.gammaB;
    }

    public void setGammaB(final double gammaB) {
        this.gammaB = gammaB;
    }
}
